﻿/// <reference path="General.js" />
/// <reference path="StringBuilder.js" />


function JsonWriter(sb)
{
	if (Function.IsInherit(arguments)) return;

	this._sb = sb;
	this._states = new Array();
}
JsonWriter._builtinKeywords = ["if", "else", "while", "do", "if", "class", "function", "this"];

JsonState =
{
	None: 0,
	Object: 11,
	ObjectValue: 12,
	Array: 21
};

JsonWriter.prototype._sb = new StringBuilder();
JsonWriter.prototype._states = new Array();
JsonWriter.prototype._curState = JsonState.None;
JsonWriter.prototype._first = true;
JsonWriter.prototype._firstDone = false;

JsonWriter.prototype.get_CurrentState = function get_CurrentState()
{
	return this._curState;
	//	if (this._states.length == 0)
	//		return JsonState.None;
	//	return this._states[this._states.length - 1];
}
JsonWriter.prototype._PushState = function _PushState(state)
{
	if (this._curState == JsonState.ObjectValue)
		this._states.pop();
	this._states.push(state);
	this._curState = state;
	this._first = true;
	this._firstDone = false;
}
JsonWriter.prototype._PopState = function _PopState()
{
	this._states.pop();
	if (this._states.length == 0)
		this._curState = JsonState.None;
	else
		this._curState = this._states[this._states.length - 1];
	this._first = false;
	this._firstDone = false;
}
//JsonWriter.prototype._ReplaceState = function _ReplaceState(state)
//{
//	this._states.pop();
//	this._states.push(this._curState = state);
//}
JsonWriter.prototype._VerifyState = function _VerifyState(states)
{
	for (var i = 0; i < states.length; ++i)
	{
		if (states[i] == this._curState)
			return;
	}
	Throw("Expected states " + states.join(", ") + " but encountered " + curState);
}
JsonWriter.prototype._VerifyStateValue = function _VerifyStateValue()
{
	this._VerifyState([JsonState.None, JsonState.ObjectValue, JsonState.Array]);
}
JsonWriter.prototype._CheckStart = function _CheckStart()
{
	if (this._curState == JsonState.Array || this._curState == JsonState.Object)
	{
		if (!this._first && !this._firstDone)
			this._sb.Append(",");
		this._first = false;
		this._firstDone = true;
	}
}
JsonWriter.prototype.Write = function Write(str)
{
	this._sb.Append(str);
}
JsonWriter.prototype.StartObject = function StartObject()
{
	this._VerifyStateValue();

	//	this._CheckArrayStart();
	this._CheckStart();

	this._PushState(JsonState.Object);
	this._sb.Append("{");
}
JsonWriter.prototype.EndObject = function EndObject()
{
	this._VerifyState([JsonState.Object]);

	this._PopState();
	this._sb.Append("}");
}
JsonWriter.prototype.StartArray = function StartArray()
{
	this._VerifyStateValue();

	this._PushState(JsonState.Array);
	this._sb.Append("[");
}
JsonWriter.prototype.EndArray = function EndArray()
{
	this._VerifyState([JsonState.Array]);

	this._PopState();
	this._sb.Append("]");
}
JsonWriter.prototype.PropertyName = function PropertyName(name)
{
	this._VerifyState([JsonState.Object]);
	//	this._CheckObjectStart();
	this._CheckStart();

	this._PushState(JsonState.ObjectValue);

	var builtin = false;
	for (var i = 0; i < JsonWriter._builtinKeywords.length; ++i)
	{
		if (JsonWriter._builtinKeywords[i] == name)
		{
			builtin = true;
			break;
		}
	}
	if (builtin)
	{
		this._sb.Append("\"");
		this._sb.Append(name);
		this._sb.Append("\"");
	}
	else
		this._sb.Append(name);
		
	this._sb.Append(":");
}
JsonWriter.prototype.Value = function Value(value)
{
	this._VerifyStateValue();

	//	this._CheckArrayStart();
	this._CheckStart();

	if (value == null)
		this._sb.Append("null");
	else if (typeof (value.Serialize) === "function")
		value.Serialize(this);
	else if (typeof (value) === "object")
		Object.Serialize(value, this);
	else
		Throw("Failed to get JSON for value " + value.toString());

	if (this._curState == JsonState.ObjectValue)
		this._PopState();
}
JsonWriter.prototype.Property = function Property(name, value)
{
	this.PropertyName(name);
	this.Value(value);
}










Object.Serialize = function Serialize(obj, jw) // need to do "static" because extending Object causes problems when enumerating using for loop
{
	jw.StartObject();

	for (var i in obj)
	{
		jw.Property(i, obj[i]);
	}

	jw.EndObject();
}
Array.prototype.Serialize = function Serialize(jw)
{
	jw.StartArray();

	for (var i = 0; i < this.length; ++i)
	{
		jw.Value(this[i]);
	}

	jw.EndArray();
}
Number.prototype.Serialize = function Serialize(jw)
{
	jw.Write(this.toString());
}
Boolean.prototype.Serialize = function Serialize(jw)
{
	jw.Write(this.toString());
}
Date.prototype.Serialize = function Serialize(jw)
{
	jw.Write("\"");
	jw.Write(this.getUTCFullYear());
	jw.Write("-");
	jw.Write(this.getUTCMonth() + 1);
	jw.Write("-");
	jw.Write(this.getUTCDate());
	jw.Write("T");
	jw.Write(this.getUTCHours());
	jw.Write(":");
	jw.Write(this.getUTCMinutes());
	jw.Write(":");
	jw.Write(this.getUTCSeconds());
	jw.Write("Z");
}
String.prototype.Serialize = function Serialize(jw)
{
	jw.Write("\"");

	for (var i = 0; i < this.length; ++i)
	{
		switch (this.charAt(i))
		{
			case '\r':
				jw.Write("\\r");
				break;
			case '\n':
				jw.Write("\\n");
				break;
			case '\t':
				jw.Write("\\t");
				break;
			case '\f':
				jw.Write("\\f");
				break;
			case '\b':
				jw.Write("\\b");
				break;
			case '\\':
				jw.Write("\\\\");
				break;
			default:
				if (this.charAt(i) == "\\" && i < this.length - 1)
				{
					if ("btnfr\"\\".indexOf(this.charAt(i + 1)) != -1)
					{
						jw.Write("\\\\");
						jw.Write(this.charAt(i + 1));
						if (this.charAt(i + 1) == "\\")
							jw.Write("\\");
						++i;
						continue;
					}
				}
				jw.Write(this.charAt(i));
				break;
		}
	}

	jw.Write("\"");
}



































function IJsonSerializable()
{
}

IJsonSerializable.prototype.Serialize = function Serialize(jw)
{
}
IJsonSerializable.prototype.get_IsDirty = function get_IsDirty()
{
}



function BaseJsonSerializable()
{
}
BaseJsonSerializable.Inherit(IJsonSerializable);

BaseJsonSerializable.prototype.Serialize = function Serialize(jw)
{
	this.SerializeObject(jw);
}
BaseJsonSerializable.prototype.SerializeObject = function SerializeObject(jw)
{
	jw.StartObject();
	this.SerializeContents(jw);
	jw.EndObject();
}
BaseJsonSerializable.prototype.SerializeArray = function SerializeArray(jw)
{
	jw.StartArray();
	this.SerializeContents(jw);
	jw.EndArray();
}
BaseJsonSerializable.prototype.SerializeContents = function SerializeContents()
{
}
BaseJsonSerializable.prototype.get_IsDirty = function get_IsDirty()
{
}










function IJsonDeserializable()
{
}

IJsonDeserializable.prototype.Deserialize = function Deserialize(json)
{
	this.DeserializeObject(json);
}



function BaseJsonDeserializable()
{
}

BaseJsonDeserializable.prototype.Deserialize = function Deserialize(json)
{
	this.DeserializeObject(json);
}
BaseJsonDeserializable.prototype.DeserializeCommon = function DeserializeCommon(json)
{
	var obj = json;
	if (typeof (json) == "string")
	{
		try
		{
			obj = eval("(" + json + ")");
		}
		catch (ex)
		{
			Throw("Text doesn't contain valid JSON.");
		}
	}
	return obj;
}
BaseJsonDeserializable.prototype.DeserializeObject = function DeserializeObject(json)
{
	var obj = BaseJsonDeserializable.prototype.DeserializeCommon(json);
	if (obj instanceof Array)
		Throw("JSON error: Object expected, found Array with " + obj.length + " items.");

	for (var prShortName in obj)
	{
		this.DeserializeContent(prShortName, obj[prShortName]);
	}
}
BaseJsonDeserializable.prototype.DeserializeArray = function DeserializeArray(json)
{
	var obj = BaseJsonDeserializable.prototype.DeserializeCommon(json);
	if (!(obj instanceof Array))
		Throw("JSON error: Array expected, found '" + obj + "', JSON input: " + json);

	for (var i = 0; i < obj.length; ++i)
	{
		this.DeserializeContent(i, obj[i]);
	}
}
BaseJsonDeserializable.prototype.DeserializeContent = function DeserializeContent(prShortName, value)
{
}


















function JsonCompatibleCollection()
{
	if (Function.IsInherit(arguments)) return;
	
	this.Array();
}
JsonCompatibleCollection.Inherit(Array);
JsonCompatibleCollection.Inherit(BaseJsonSerializable);
JsonCompatibleCollection.Inherit(BaseJsonDeserializable);
if (_____a)
{
	JsonCompatibleCollection.prototype = new Array();
	JsonCompatibleCollection.prototype.Array = new Array();
}

JsonCompatibleCollection.prototype.get_IsDirty = function get_IsDirty()
{
	return this.length > 0;
}
JsonCompatibleCollection.prototype.Serialize = function Serialize(jw)
{
	this.SerializeArray(jw);
}
JsonCompatibleCollection.prototype.SerializeContents = function SerializeContents(jw)
{
	for (var i = 0; i < this.length; ++i)
	{
		this[i].Serialize(jw);
	}
}

JsonCompatibleCollection.prototype.Deserialize = function Deserialize(json)
{
	this.DeserializeArray(json);
}
JsonCompatibleCollection.prototype.DeserializeContent = function DeserializeContent(prShortName, value)
{
	var t = this.CreateOneItem(value);
	t.Deserialize(value);
	this.push(t);
}
JsonCompatibleCollection.prototype.CreateOneItem = function CreateOneItem(value)
{
}
